package cn.iocoder.yudao.module.ym.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import java.util.ArrayList;
import java.util.List;

public class PolygonChecker {

    /**
     * 判断点是否在任意多边形内
     * @param point 目标点 [经度, 纬度]
     * @param jsonString JSON格式的多边形数据
     * @return true: 点在任意多边形内或边界上, false: 点不在任何多边形内
     */
    public static boolean isPointInAnyPolygon(double[] point, String jsonString) {
        // 预检测：检查点是否在多边形的包围盒内
        List<double[][]> polygons = extractPolygons(JSON.parse(jsonString));


        return polygons.stream().anyMatch(polygon ->
                 isPointInPolygon(point, polygon)
        );
    }

    /**
     * 递归提取所有多边形（支持2D/3D/4D数据结构）
     */
    private static List<double[][]> extractPolygons(Object data) {
        List<double[][]> result = new ArrayList<>();

        if (data instanceof JSONArray) {
            JSONArray arr = (JSONArray) data;
            if (arr.isEmpty()) return result;

            // 检查是否为点数组（最内层）
            if (isPointArray(arr)) {
                double[][] polygon = parseRing(arr);
                if (polygon.length >= 3) { // 多边形至少需要3个点
                    result.add(polygon);
                }
                return result;
            }

            // 处理嵌套的多边形数组
            for (int i = 0; i < arr.size(); i++) {
                Object element = arr.get(i);
                if (element instanceof JSONArray) {
                    result.addAll(extractPolygons(element));
                }
            }
        }
        return result;
    }

    /**
     * 判断是否为点数组（最内层结构）
     */
    private static boolean isPointArray(JSONArray arr) {
        if (arr.isEmpty()) return false;
        Object first = arr.get(0);

        if (first instanceof JSONArray) {
            JSONArray inner = (JSONArray) first;
            return !inner.isEmpty() && inner.get(0) instanceof Number;
        }
        return first instanceof Number;
    }

    /**
     * 将JSONArray转为多边形环
     */
    private static double[][] parseRing(JSONArray ringArray) {
        List<double[]> points = new ArrayList<>();
        for (int i = 0; i < ringArray.size(); i++) {
            Object element = ringArray.get(i);

            if (element instanceof JSONArray) {
                JSONArray pointArr = (JSONArray) element;
                if (pointArr.size() >= 2) {
                    points.add(new double[]{
                            pointArr.getDouble(0),
                            pointArr.getDouble(1)
                    });
                }
            }
        }
        return points.toArray(new double[0][]);
    }

    /**
     * 射线法判断点是否在多边形内
     */
    private static boolean isPointInPolygon(double[] point, double[][] polygon) {
        if (polygon.length < 3) return false;

        double px = point[0];
        double py = point[1];
        boolean inside = false;

        // 先检查点是否在粗略的包围盒内（性能优化）
        double minX = polygon[0][0], maxX = polygon[0][0];
        double minY = polygon[0][1], maxY = polygon[0][1];
        for (double[] p : polygon) {
            if (p[0] < minX) minX = p[0];
            if (p[0] > maxX) maxX = p[0];
            if (p[1] < minY) minY = p[1];
            if (p[1] > maxY) maxY = p[1];
        }
        if (px < minX || px > maxX || py < minY || py > maxY) {
            return false;
        }

        // 射线法核心算法
        for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
            double xi = polygon[i][0], yi = polygon[i][1];
            double xj = polygon[j][0], yj = polygon[j][1];

            // 检查点是否在顶点上
            if (Math.abs(px - xi) < 1e-9 && Math.abs(py - yi) < 1e-9) {
                return true;
            }

            // 检查点是否在边上
            if (isPointOnSegment(px, py, xi, yi, xj, yj)) {
                return true;
            }

            // 射线交叉检测
            if ((yi > py) != (yj > py)) {
                double slope = (xj - xi) * (py - yi);
                double intersectX = xi + (xj - xi) * (py - yi) / (yj - yi);

                if (px < intersectX) {
                    inside = !inside;
                }
            }
        }
        return inside;
    }

    /**
     * 判断点是否在线段上（考虑浮点误差）
     */
    private static boolean isPointOnSegment(double px, double py,
                                            double x1, double y1,
                                            double x2, double y2) {
        // 点在矩形范围内
        boolean inX = (px >= Math.min(x1, x2) - 1e-9) && (px <= Math.max(x1, x2) + 1e-9);
        boolean inY = (py >= Math.min(y1, y2) - 1e-9) && (py <= Math.max(y1, y2) + 1e-9);
        if (!inX || !inY) return false;

        // 共线检测（向量叉积）
        double crossProduct = (py - y1) * (x2 - x1) - (px - x1) * (y2 - y1);
        return Math.abs(crossProduct) < 1e-9;
    }

    public static double[] convertStringToDoubleArray(String input) {
        // 1. 预处理：去除空白字符和方括号
        String cleaned = input.replaceAll("\\s+", "")   // 移除所有空白字符
                .replaceAll("^\\[|\\]$", ""); // 移除开头和结尾的方括号

        // 2. 检查空字符串
        if (cleaned.isEmpty()) {
            return new double[0];
        }

        // 3. 分割字符串
        String[] parts = cleaned.split(",");
        double[] result = new double[parts.length];

        // 4. 转换每个部分
        for (int i = 0; i < parts.length; i++) {
            try {
                result[i] = Double.parseDouble(parts[i]);
            } catch (NumberFormatException e) {
                // 处理无效数值
                throw new IllegalArgumentException("无法解析的数值: '" + parts[i] +
                        "', 位置: " + i, e);
            }
        }

        return result;
    }

    // 测试用例
    public static void main(String[] args) {


        // 清远市多边形数据
        String qingyuanData = "[[[113.90224485447641,34.78909235034211],[113.92384870661728,34.78909235034211],[113.90224485447641,34.75332238075021],[113.92384870661728,34.75332238075021]]]";
        String s = "[113.90804678054684, 34.78120736554616]";
        double[] doubles = convertStringToDoubleArray(s);
        for (double aDouble : doubles) {
            System.out.println(aDouble);
        }

        // 测试点是否在清远市多边形内
        boolean inQingyuan = isPointInAnyPolygon(doubles, qingyuanData);
        System.out.println("点是否在多边形内: " + inQingyuan); // 应为 true
    }
}
